-module(ip_parser).
-include("protocol.hrl").
-behaviour(gen_server).
-export([start/0,init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2,code_change/3]).
-export([parse/1,do/1]).
-define(SERVER,?MODULE).

-record(ip_counter,{
		v4 = 0,
		illegal = 0,
		unknown = 0
		}).
parse(Packet_Tuple) ->
	gen_server:call(?SERVER,{packet_comming,Packet_Tuple}).
do(stop) ->
	gen_server:call(?SERVER,stop);
do(report) ->
	Counter = gen_server:call(?SERVER,report),
	io:format("IP Counter ~p ~n",[Counter]).
	
start()  ->
	case gen_server:start({local, ?SERVER}, ?MODULE, [], []) of 
	{ok,_} ->
		ok;
	{error,{already_started,Pid}} ->
		ok;
	{error,Reason} ->
		io:format("start ~s service failure with ~p~n",[?SERVER,Reason])
	end.
init(_) ->
	io:format("ip_parser start...~n"),
	{ok,{running,#ip_counter{}}}.
	
handle_call({packet_comming,P_Tuple},_,{running,Counter}) ->
	case P_Tuple#packet_tuple.packet of
	<<4:4,_HdrLen:4,_Tos:8,Len:16,
	  _Id:16,Flags:3,_FragOffset:13,
	  _TTL:8,Protocol:8,_CheckSum:16,
	  S1:8/integer,S2:8/integer,S3:8/integer,S4:8/integer,
	  D1:8/integer,D2:8/integer,D3:8/integer,D4:8/integer,
	  Payload/bytes>> ->
		Next_P = packet_dispatch:protocol_lookup(ip_protocol,Protocol),
		packet_dispatch:do(Next_P,P_Tuple#packet_tuple{packet=Payload,size = P_Tuple#packet_tuple.size - 20,
														context = [#ip_protocol{source_ip={S1,S2,S3,S4},dest_ip={D1,D2,D3,D4}}|P_Tuple#packet_tuple.context]}),
		{reply,ok,{running,
							if 
								Next_P =:= unknown ->
									Counter#ip_counter{unknown = Counter#ip_counter.unknown + 1};
								true ->
									Counter#ip_counter{v4 = Counter#ip_counter.v4 + 1}
							end}};
	_ ->
		io:format("illegal ip Packet ~p ~n",[P_Tuple#packet_tuple.packet]),
		{reply,{error,unknown},{runing,Counter#ip_counter{illegal = Counter#ip_counter.illegal + 1}}}
	end;
	
handle_call(report,_,{State,Counter}) ->
	{reply,Counter,{State,Counter}};

handle_call(reset_counter,_,{State,Counter}) ->
	{noreply,{State,#ip_counter{}}};
handle_call(stop,_,{_,Counter}) ->
	{stop,caller_initiative,{stop,Counter}};
	
handle_call(_,_,State) ->
	{reply, {error,"no func define"}, State}.
handle_cast(_, State) ->
	{noreply,State}.
handle_info(_, State) ->
	{noreply,State}.
terminate(_, _) ->
	ok.
code_change(_, State, _) ->
	{ok,State}.	
